/*
 * Copyright (c) 2001, 2002 Boris Pruessmann and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Common Public License v0.5 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 */
package net.sourceforge.perforce.core.internal.api;

import java.util.Map;

import net.sourceforge.perforce.api.IError;
import net.sourceforge.perforce.api.IPerforceCommand;
import net.sourceforge.perforce.api.internal.Error;
import net.sourceforge.perforce.api.internal.Spec;
import net.sourceforge.perforce.api.internal.SpecData;
import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IChangeResult;
import net.sourceforge.perforce.core.resources.IChangelist;

import org.apache.regexp.RE;
import org.apache.regexp.RECompiler;
import org.apache.regexp.REProgram;
import org.apache.regexp.RESyntaxException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;

/**
 * ClientUser object for the 'p4 change' command.
 * 
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class Change extends AbstractPerforceCommand implements IPerforceCommand {
  // --------------------------------------------------------------------
  // Private Static Constants

  /** Regular expression: Change <id> created. */
  private static final REProgram RE_CHANGE_CREATED;

  // -------------------------------------------------------------------- Static
  // Initializer Block

  static {
    try {
      RECompiler compiler = new RECompiler();
      RE_CHANGE_CREATED = compiler.compile("^Change (.*) created.*"); //$NON-NLS-1$
    } catch (RESyntaxException e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the result of the operation. */
  private Result result;
  /** Stores the id of the created changelist (when applicable) */
  private Integer integerResult;
  /** Stores the change specification. */
  private String changeSpec;

  // --------------------------------------------------------------- Public
  // Static Factory Methods

  /**
   * Convenience method for creating instances of Change.
   * 
   * @param changeSpec input parameter to the commands.
   * @return the Change instance.
   */
  public static final Change newInstanceForOutput(String changeSpec) {
    MultiStatus status = new MultiStatus(PerforceCorePlugin.ID, IStatus.OK, "", null);

    return new Change(status, changeSpec);
  }

  /**
   * Convenience method for creating instances of Change.
   * 
   * @return Change object used for creating a changelist.
   */
  public static final Change newInstanceForInput() {
    MultiStatus status = new MultiStatus(PerforceCorePlugin.ID, IStatus.OK, "", null);

    return new Change(status);
  }

  // ------------------------------------------------------------------------
  // Private Constructors

  /**
   * Constructor for Change.
   * 
   * @param status the MultiStatus.
   * @param changeSpec the change specifiaction.
   */
  private Change(MultiStatus status, String changeSpec) {
    super(status);
    this.changeSpec = changeSpec;
  }

  /**
   * Constructor for Change.
   * 
   * @param status the multi status.
   */
  private Change(MultiStatus status) {
    super(status);
  }

  // -------------------------------------------------------- Public Methods
  // (IClientUserDelegate)

  @Override
  public String inputData(IError error) {
    return changeSpec;
  }

  @Override
  public void outputStat(Map<String, ?> strDict) {
    if (strDict.containsKey("specdef") && strDict.containsKey("data")) {
      Error error = new Error();

      result = new Result();
      result.getChangeSpecDefinition().decode((String) strDict.get("specdef"), error);
      result.getChangeSpecDefinition().parse((String) strDict.get("data"),
          result.getChangeSpecData(), error, 0);

      if (error.test()) {
        getMultiStatus().merge(
            new Status(IStatus.ERROR, PerforceCorePlugin.ID, error.getGeneric(), error
                .format(IError.EF_PLAIN), null));
      }

      error.dispose();
    }
  }

  @Override
  public void outputInfo(char level, String infoString) {
    RE re = new RE(RE_CHANGE_CREATED);
    if (re.match(infoString)) {
      try {
        integerResult = new Integer(Integer.parseInt(re.getParen(1)));
      } catch (NumberFormatException e) {
        integerResult = IChangelist.DEFAULT;
      }
    }
  }

  // ----------------------------------------------------------- Public Methods
  // (IPerforceCommand)

  public String getCommand() {
    return "change"; //$NON-NLS-1$
  }

  @Override
  public IStatus getStatus() {
    return getMultiStatus();
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  /**
   * Returns the result of the operation.
   * 
   * @return the result.
   */
  public Object getResult() {
    if (result != null) {
      return result;
    } else {
      return integerResult;
    }
  }

  // ----------------------------------------------------------------------
  // Private Nested Classes

  /** Result object that is returned as a result of the operation. */
  private static class Result implements IChangeResult {
    // -----------------------------------------------------------------------
    // Private Variables

    /** Stores the changelist specificaiton. */
    private Spec spec = new Spec();
    /** Stores the data of the specification. */
    private SpecData specData = new SpecData();

    //--------------------------------------------------------------------------
    // Public Methods

    /** @see IChangeResult#getChangeSpecData() */
    public SpecData getChangeSpecData() {
      return specData;
    }

    /** @see IChangeResult#getChangeSpecDefinition() */
    public Spec getChangeSpecDefinition() {
      return spec;
    }
  }
}
